#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <cerrno>
#include <cassert>
#include <memory>
#include <signal.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include "ThreadPool/log.hpp"
#include "ThreadPool/threadPool.hpp"
#include "ThreadPool/Task.hpp"

// 服务器提供的读取和写入操作(之前)
// static void service(int sock, const std::string &clientip, const uint16_t &clientport)
// {
//     char buffer[1024];
//     while (true)
//     {
//         // 读取缓冲区内容，有内容输出，没有内容写入，否则错误
//         ssize_t s = read(sock, buffer, sizeof(buffer));
//         if (s > 0)
//         {
//             buffer[s] = 0; // s为返回读取的字节数,目的是当成字符串处理
//             std::cout << clientip << ":" << clientport << "||" << buffer << std::endl;
//         }
//         else if (s == 0)
//         {
//             logMessage(NORMAL, "%s:%d shutdown,me too!", clientip.c_str(), clientport);
//             break;
//         }
//         else
//         {
//             logMessage(ERROR, "read socket error,%d:%s", errno, strerror(errno));
//             break;
//         }

//         write(sock, buffer, strlen(buffer));
//     }
// }

//服务器中多一个线程名参数
static void service(int sock, const std::string &clientip,
                    const uint16_t &clientport, const std::string &thread_name)
{
    // echo server
    //  同时在线10人
    //  所以，我们一般服务器进程业务处理，如果是从连上，到断开，要一直保持这个链接, 长连接
    //  后面有其他方案！
    char buffer[1024];
    while (true)
    {
        // read && write 可以直接被使用！
        ssize_t s = read(sock, buffer, sizeof(buffer) - 1);
        if (s > 0)
        {
            buffer[s] = 0; // 将发过来的数据当做字符串
            std::cout << thread_name << "|" << clientip << ":" << clientport << "# " << buffer << std::endl;
        }
        else if (s == 0) // 对端关闭连接
        {
            logMessage(NORMAL, "%s:%d shutdown, me too!", clientip.c_str(), clientport);
            break;
        }
        else
        { //
            logMessage(ERROR, "read socket error, %d:%s", errno, strerror(errno));
            break;
        }

        write(sock, buffer, strlen(buffer));
    }
    close(sock);
}

class ThreadData
{
public:
    int _sock;
    std::string _ip;
    uint16_t _port;
    std::string _threadname;
};

class TcpServer
{
private:
    const static int gbacklog = 20;
    //线程处理任务的方法(静态成员函数没有this指针的问题)
    static void*threadRoutine(void*args)
    {
        //创建的线程进行线程分离
        pthread_detach(pthread_self());
        ThreadData*td = static_cast<ThreadData*>(args);
        service(td->_sock,td->_ip,td->_port,td->_threadname);
        delete td;

        return nullptr;
    }

private:
    uint16_t _port;
    std::string _ip;
    int listensock;
    //线程池（初始化，线程的单例模式）
    std::unique_ptr<ThreadPool<Task>> _threadpool_ptr;

public:
    TcpServer(uint16_t port, std::string ip = "") : listensock(-1), _port(port), _ip(ip)
    ,_threadpool_ptr(ThreadPool<Task>::getThreadPool())
    {
    }

    void initServer()
    {
        // 1. 创建套件字（网络协议+TCP）
        listensock = socket(AF_INET, SOCK_STREAM, 0);
        if (listensock < 0)
        {
            logMessage(FATAL, "create socket error, %d:%s", errno, strerror(errno));
            exit(2);
        }
        logMessage(NORMAL, "create socket success, listensock: %d", listensock);

        // 2.给该进程绑定指定的端口号（待深入理解）
        struct sockaddr_in local;
        memset(&local, 0, sizeof local);
        local.sin_family = AF_INET;
        local.sin_port = htons(_port); // 转换为主机端口
        local.sin_addr.s_addr = _ip.empty() ? INADDR_ANY : inet_addr(_ip.c_str());
        if (bind(listensock, (struct sockaddr *)&local, sizeof(local)) < 0)
        {
            logMessage(FATAL, "bind error, %d:%s", errno, strerror(errno));
            exit(3);
        }

        // 3.建立连接，因为TCP是面向连接的通信方法
        if (listen(listensock, gbacklog) < 0)
        {
            logMessage(FATAL, "listen error, %d:%s", errno, strerror(errno));
            exit(4);
        }
        logMessage(NORMAL, "init server success");
    }

    void start()
    {
        //设置SIGCHLD信号自动忽略，所以子进程退出的时候，会自动的进行释放
        // signal(SIGCHLD, SIG_IGN); // 防止子进程成为僵尸进程，设置让其自己释放（多线程2.0实现使用）

        //版本4.0 启动线程池
        _threadpool_ptr->run();

        while (true)
        {
            // 4.获取连接
            struct sockaddr_in src;
            socklen_t len = sizeof src;
            // accept()是专门负责找客人的张三；servicesock是负责接待客人的李四王五
            int servicesock = accept(listensock, (struct sockaddr *)&src, &len);
            // 获取连接失败
            if (servicesock < 0)
            {
                logMessage(ERROR, "accept error, %d:%s", errno, strerror(errno));
                continue;
            }
            // 获取连接成功（将端口号和IP转换成网络格式
            uint16_t client_port = ntohs(src.sin_port);
            std::string client_ip = inet_ntoa(src.sin_addr);
            logMessage(NORMAL, "link success, servicesock: %d | %s : %d |\n",
                       servicesock, client_ip.c_str(), client_port);

            //单进程通信版本1.0
            // service(servicesock,client_ip,client_port);

            //多进程通信版本2.0
            // pid_t id = fork();
            // assert(id!=-1);//如果返回-1，则子进程创建失败
            // if(id==0)
            // {
            //     close(listensock);
            //     service(servicesock,client_ip,client_port);
            //     exit(0);
            // }
            // close(servicesock);
            //使用完servicesock一定要及时关闭该套接字，以避免可用的文件描述符越来越少

            //多进程版本2.1
            // pid_t id = fork();
            // //子进程创建后，生成孙子进程去执行服务任务，然后自己退出，让操作系统自动回收，从而避免阻塞
            // if(id==0)
            // {
            //     close(listensock);//关闭父进程使用的套接字，子进程使用自己的
            //     //fork（）大于0时，表示的是子进程本身，所以如果子进程创建成功，则子进程本身就退出
            //     if(fork()>0) exit(0);
            //     //子进程退出后会有孙子进程，所以孙子进程被操作系统领养，回收由操作系统
            //     service(servicesock,client_ip,client_port);
            // }
            // //因为子进程创建成功后就关闭了，所以父进程这里不会发生阻塞
            // waitpid(id,nullptr,0);
            // close(servicesock);

            //3.0版本：多线程版本
            //线程的基本属性封装到一个类中;为保证线程安全，需要将线程数据放到堆空间中进行相应赋值
            // ThreadData *td = new ThreadData();
            // td->_ip = client_ip;
            // td->_port = client_port;
            // td->_sock = servicesock;
            // pthread_t tid;
            // pthread_create(&tid,nullptr,threadRoutine,td);
            // close(servicesock);

            //4.0版本：线程池版本（设计一个线程池去处理客户端提供的任务）
            Task t (servicesock,client_ip,client_port,service);
            _threadpool_ptr->pushTask(t);
        }
    }

    ~TcpServer(){}

};